<?php
namespace App\Service\VendorClient\Shopwangdt;

use App\Service\Code\Normal;
use App\Service\BaseService;
use App\Model\PssModel\ServiceConfig;
use Service\AppService\IdService;
use App\Service\TaskServer\TaskWorker;

/**
 * 旺店通(企业版)上游对接(一般贸易)SDK
 * @author hj
 */
class ShopwangdtApiClient extends BaseService {
    //供应商编号
    const SUPPLIER_CODE = 'shopwangdt';

    //配置文件
    private $config = [];

    public function __construct($apiBody = []) {
        $this->config = $apiBody;
    }

    /**
     * 创建订单
     * @param array $params
     * @return array
     */
    public function createOrder($param = []) {
        if (empty($param['supplierOrder']) || empty($param['orderGoods']) || empty($param['order']) || empty($param['binfo'])) {
            return Normal::returnCode(Normal::FAIL, "except param supplierOrder or orderGoods or order or binfo");
        }
        $orderInfo = $param['supplierOrder'];
        $goodsList = $param['orderGoods'];
        $order     = $param['order'];
        $binfo     = $param['binfo'];
        $order_sn  = $orderInfo['supplier_dsn'];
        //订单时间
        $order_time = $orderInfo['cd'] ? date('Y-m-d H:i:s', $orderInfo['cd']) : date('Y-m-d H:i:s', time());
        //支付时间
        $pay_time = isset($order['payTime']) && !empty($order['payTime']) ? date('Y-m-d H:i:s', $order['payTime']) : date('Y-m-d H:i:s', time());
        //运费分摊
        $deliver_money = $orderInfo['deliver_money'];
        //优惠
        $discount_money = $orderInfo['discount_money'];
        //下单支付的时候支付信息
        $apply_extends = @json_decode($orderInfo['apply_extends'], true);
        //用户实际支付金额
        $real_total_money = $orderInfo['real_total_money'];
        //获取下单商品信息
        $palceOrderGoods  = [];
        $goodsMoneyOrigin = 0;
        foreach ($goodsList as $v) {
            $goodsMoneyOrigin = bcadd($goodsMoneyOrigin, bcmul($v['price'], $v['quantity'], 2), 2);
        }
        $hasDiscountMoney = $order_pay_amount = $channel_discount_amount = $goodsTotalMoney = 0;
        $goodsNum         = count($goodsList);
        foreach ($goodsList as $key => $item) {
            $price            = (string)$item['price'];
            $spec_num         = (string)$item['spec_num'];
            $quantity         = (string)$item['quantity'];
            $supplier_skucode = $item['supplier_skucode'];
            $goods_type       = $item['goods_type'];
            $buyNum           = $spec_num * $quantity;

            //分摊优惠金额到单个商品
            if ($key == $goodsNum - 1) {
                $skuDiscountAmountTotal = bcsub((string)$discount_money, (string)$hasDiscountMoney, 2);
            } else {
                $singleGoodsTotalPrice  = bcmul((string)$price, (string)$quantity, 2);//107
                $scale                  = bcdiv((string)$singleGoodsTotalPrice, (string)$goodsMoneyOrigin, 4);//0.6903
                $skuDiscountAmountTotal = bcmul((string)$scale, (string)$discount_money, 2);
                $hasDiscountMoney       = bcadd((string)$hasDiscountMoney, (string)$skuDiscountAmountTotal, 2);
            }
            //原始销售单价
            $singlePrice = bcdiv((string)$price, (string)$spec_num, 3);
            $singlePrice = ceil(bcmul((string)$singlePrice, '100', 1));
            $singlePrice = bcdiv((string)$singlePrice, '100', 2);
            //向上取整3位收上去的
            $singleNewTotalPrice = bcmul((string)$singlePrice, (string)$buyNum, 2);
            $signleOldTotalPrice = bcmul((string)$price, (string)$quantity, 2);
            $diff_price          = bcsub((string)$singleNewTotalPrice, (string)$signleOldTotalPrice, 2);
            //优惠金额还需要加上补差金额否则无法过去
            $skuDiscountAmountTotal = bcadd((string)$diff_price, (string)$skuDiscountAmountTotal, 2);
            //百大特殊逻辑 实际支付金额为0 但是有运费  他们实际上是把运费摊到优惠金额上了
            //这样就会导致 商品优惠金额大于商品单价
            if($real_total_money == 0){
                $newSkuDiscountAmountTotal = bcmul($singlePrice, $buyNum, 2);
            }else{
                $newSkuDiscountAmountTotal = $skuDiscountAmountTotal;
            }
            //下单商品组
            $goodsInfo = [
                'oid'            => $item['id'],//平台订单货品表主键,子订单唯一标识
                'num'            => $buyNum,//货品数量，订单推送成功以后本字段值不能更改
                'price'          => $singlePrice,//标价,折扣前的价格,可以推送价格为0的商品
                'status'         => 30,//平台子订单状态，子订单状态可以和主订单不一样可选值同trade_status
                'refund_status'  => 0,//0:无退款,1:取消退款,2:已申请退款,3:等待退货,4:等待收货,5:退款成功
                'goods_id'       => trim($item['supplier_commodity_key']),//平台系统货品（SPU）的唯一标识。goods_id不能为空
                'spec_no'        => trim($item['supplier_skucode']),//平台货品SKU唯一码，对应ERP商家编码，goods_no和spec_no不能同时为空
                'goods_name'     => $item['goods_name'],//名称
                'adjust_amount'  => 0,//客服调整总金额(大于0加价，小于0减价，是折扣来源的一部分,没有传0)
                'discount'       => 0,//下单总折扣，客户下单时折扣(比如促销打折，不包含客服调整、分摊折扣，没有传0)
                'share_discount' => $newSkuDiscountAmountTotal,//分摊总折扣，由总订单分摊而来，一般是付款时产生，如使用优惠券，没有传0。分摊优惠传值注意：例如三个商品，优惠10，分摊优惠可以是：3/3/4，或者3.33/3.33/3.34.即最后一个商品的分摊优惠使用减法计算
            ];
            // paid = Σ(price * num + adjust_amount -discount – share_discount）+ post_amount+other_amount
            $palceOrderGoods[]       = $goodsInfo;
            $goodsTotalMoney         = bcadd((string)$goodsTotalMoney, (string)$singleNewTotalPrice, 2);
            $channel_discount_amount = bcadd((string)$channel_discount_amount, (string)$skuDiscountAmountTotal, 2);
        }
        //百大特殊逻辑 实际支付金额为0 但是有运费  他们实际上是把运费摊到优惠金额上了
        //这样就会导致 商品优惠金额大于商品单价
        if($real_total_money==0 && $deliver_money > 0){
            $real_total_money = $deliver_money;
        }else{
            //正常逻辑 订单总价（商品保税价格 + 运费） 暂时不这样计算
            $totalMoney       = bcadd((string)$goodsTotalMoney, (string)$deliver_money, 2);//分摊计算出来的订单总金额
            $order_pay_amount = bcsub((string)$totalMoney, (string)$channel_discount_amount, 2);//实际支付总金额;
            if ($order_pay_amount != $real_total_money) {
                return Normal::returnCode(Normal::FAIL, "金额分摊计算有误");
            }
        }
        $trade_list[] = [
            'tid'               => $order_sn,//指商城、官网等平台的订单编号，ERP称之为原始单号
            'trade_status'      => 30,//trade_status=30 已付款待发货(包含货到付款)，30只可以直接变更为70/ 80这2种状态
            'pay_status'        => 2,//平台订单付款状态:0:未付款,1:部分付款,2:已付款
            'delivery_term'     => 1,//1:款到发货,2:货到付款(包含部分货到付款),3:分期付款,4:挂账
            'trade_time'        => $order_time,//平台订单创建时间,时间格式：yyyy-MM-dd HH:mm:ss
            'pay_time'          => $pay_time,//平台订单付款时间,时间格式：yyyy-MM-dd HH:mm:ss，未付款订单为:0000-00-00 00:00:00
            'buyer_nick'        => $orderInfo['buyer_realname'],//平台买家昵称，注意：一次推送网名相同的订单不能超过30
            'receiver_name'     => $orderInfo['receive_name'],
            'receiver_province' => $orderInfo['province'],
            'receiver_city'     => $orderInfo['city'],
            'receiver_district' => $orderInfo['district'],
            'receiver_address'  => $orderInfo['receive_address'],
            'receiver_mobile'   => $orderInfo['recveive_phone'],
            'post_amount'       => $deliver_money,//商家收取买家的物流或者快递费用
            'cod_amount'        => '0.00',//货到付款金额   cod金额=(price * num + adjust_amount -discount – share_discount）+post_amount+ext_cod_fee-paid
            'ext_cod_fee'       => '0.00',//货到付款买家费用，扣除货到付款订单金额后，卖家仍需支付的货到付款其他金额
            'other_amount'      => '0.00',//其它应从买家收取的服务费，其他费用
            'paid'              => $real_total_money,//订单已付金额，paid计算公式：paid = Σ(price * num + adjust_amount -discount – share_discount）+ post_amount+other_amount，所有金额相关字段推送处理办法
            'id_card_type'      => 1,//1:身份证（不传默认为0，为0时将证件号码置空）
            'id_card'           => $orderInfo['buyer_idcard'],//证件号码
            'order_list'        => $palceOrderGoods,
        ];
        $data         = [
            'method' => 'createOrder',
            'data'   => [
                'shop_no'    => $this->config['shopNo'],
                'trade_list' => json_encode($trade_list, JSON_UNESCAPED_UNICODE)
            ],
        ];
        return $this->execute($data);
    }

    /**
     * 商品列表查询
     * @param $data
     * @return array
     */
    public function queryGoodsList($param = []) {
        $page      = $param['page'] ?? 1;
        $limit     = $param['limit'] ?? 40;
        $diffSec = 2591999;//30天的秒数 2592000 - 1秒
        $endTime = time();
        $startTime = $endTime - $diffSec - 1;//start_time和end_time最大跨度为30天
        $reqData   = [
            'method' => 'queryGoods',
            'data'   => [
                'start_time' => date('Y-m-d H:i:s', $startTime),//按最后修改时间增量查询数据，start_time作为开始时间，格式：yyyy-MM-dd HH:mm:ss
                'end_time'   => date('Y-m-d H:i:s', $endTime),
                'deleted'    => 0,//默认为0,  0：只返回未删除货品    1：返回未删除和已删除货品
                'page_size'  => $limit,//每页返回的数据条数，输入值范围1~100，不传本参数，输入值默认为40
                'page_no'    => $page - 1,//页号，不传默认从0页开始
            ],
        ];
        $result    = $this->execute($reqData);
        if ($result['code'] != Normal::SUC) {
            return Normal::returnCode(Normal::FAIL, "当前商品列表数据获取异常");
        }
        if (!isset($result['data']['goods_list']) || empty($result['data']['goods_list'])) {
            return Normal::returnCode(Normal::SUC, '获取完成', ['total' => 0, 'data' => []]);
        }
        $spuList = $result['data']['goods_list'];
        $newList = [];
        foreach ($spuList as $spu) {
            if (!isset($spu['spec_list']) || empty($spu['spec_list'])) {
                continue;
            }
            if (isset($spu['deleted']) && $spu['deleted'] > 0) {
                continue;
            }
            foreach ($spu['spec_list'] as $sku) {
                $newList[] = [
                    'goods_id'         => $spu['goods_id'],
                    'goods_no'         => $spu['goods_no'],
                    'goods_name'       => $spu['goods_name'],
                    'short_name'       => $spu['short_name'],
                    'alias'            => $spu['alias'],
                    'brand_name'       => $spu['brand_name'],
                    'origin'           => $spu['origin'],
                    'class_name'       => $spu['class_name'],
                    'unit_name'        => $spu['unit_name'],
                    'properties'       => $spu['properties'],
                    'spec_id'          => $sku['spec_id'],
                    'spec_no'          => $sku['spec_no'],
                    'spec_code'        => $sku['spec_code'],
                    'barcode'          => $sku['barcode'],
                    'spec_name'        => $sku['spec_name'],
                    'lowest_price'     => $sku['lowest_price'],
                    'retail_price'     => $sku['retail_price'],
                    'wholesale_price'  => $sku['wholesale_price'],
                    'member_price'     => $sku['member_price'],
                    'market_price'     => $sku['market_price'],
                    'weight'           => $sku['weight'],
                    'tax_rate'         => $sku['tax_rate'],
                    'img_url'          => $sku['img_url'],
                ];
            }
        }
        if (empty($newList)) {
            return Normal::returnCode(Normal::SUC, '获取完成', ['total' => 0, 'data' => []]);
        }
        foreach ($newList as &$item) {
            $item['stock_num']     = 0;
            $item['avaliable_num'] = 0;
            $reqStock              = [
                'spec_no'    => $item['spec_no'],//(sku)所有属性的编码，传此参数，按照商家编码指定返回响应参数；若传该字段则不校验时间
                'is_deleted' => 1,//为1时只返回在库存管理界面未删除的库存数据信息
                'page_size'  => 10,//每页返回的数据条数，输入值范围1~100，不传本参数，输入值默认为40
                'page_no'    => 0,//不传值默认从0页开始
            ];
            $stokcRes              = $this->queryGoodsStock($reqStock);
            if ($stokcRes['code'] != Normal::SUC) {
                continue;
            }
            if (!isset($stokcRes['data']['stocks']) || empty($stokcRes['data']['stocks'])) {
                continue;
            }
            $stockArr              = reset($stokcRes['data']['stocks']);
            $item['stock_num']     = (int)($stockArr['stock_num']) ?? 0;
            $item['avaliable_num'] = (int)($stockArr['avaliable_num']) ?? 0;
            unset($item);
        }
        return Normal::returnCode(Normal::SUC, '获取成功', ['total' => count($newList), 'data' => $newList]);
    }

    /**
     * 库存查询
     * @param $data
     * @return array
     */
    public function queryGoodsStock($param = []) {
        $data   = [
            'method' => 'queryStock',
            'data'   => $param,
        ];
        return $this->execute($data);
    }

    /**
     * 查询物流
     * @param $OrderSupplier
     * @return array
     */
    public function queryOrderExpress($OrderSupplier) {
        $orderSn = $OrderSupplier['supplier_dsn'];
        $data    = [
            'method' => 'orderExpress',
            'data'   => [
                'shop_no'           => $this->config['shopNo'],
                'limit'             => 100,//每次请求需返回发货记录条数控制，取值范围大于等于，1小于等于100，limit输入值大于100时接口默认limit=100.
                'is_part_sync_able' => 0,//是否支持拆单发货 0 否 1 是（支持开启多物流单号回传的卖家，没有对应需求的卖家请不要传该字段）
            ],
        ];
        $result  = $this->execute($data);
        if ($result['code'] != Normal::SUC) {
            return $result;
        }
        $tradeList = $result['data']['trades'] ?? [];
        if (empty($tradeList)) {
            return Normal::returnCode(Normal::FAIL, '未查询到物流信息1', $result);
        }
        $tradeListByTid = array_column($tradeList, NULL, 'tid');
        $targetArr      = $tradeListByTid[$orderSn] ?? [];
        if (empty($targetArr)) {
            return Normal::returnCode(Normal::FAIL, '未查询到物流信息2', $result);
        }
        //查到了就去回传
        // $this->orderExpressAck([$targetArr['rec_id']]);//rec_id
        return Normal::returnCode(Normal::SUC, "获取成功", $targetArr);
    }

    /**
     * 物流成功回传
     * @param $recIds
     * @return array
     */
    public function orderExpressAck($recIds = []) {
        if (!$recIds) {
            return Normal::returnCode(Normal::FAIL, '缺少rec_id参数');
        }
        $ackList = [];
        foreach ($recIds as $rec_id) {
            $ackList[] = [
                'rec_id'  => $rec_id,//回写的记录id，见logistics_sync_query中rec_id
                'status'  => 0,//回写状态: 0成功 1失败
                'message' => "获取成功"
            ];
        }
        $data = [
            'method' => 'orderExpressAck',
            'data'   => [
                'logistics_list' => json_encode($ackList, JSON_UNESCAPED_UNICODE),
            ],
        ];
        return $this->execute($data);
    }

    /**
     * 申请退款接口
     * @param $data
     * @return array
     */
    public function orderAfterSale($params = []) {
        $goodsList   = $params['orderGoods'];
        $refundGoods = [];
        //订单状态（0-待付款，1-已付款，2-已发货，100-已取消，200-支付失败）
        $orderStatus = $params['supplierOrder']['order_status'];
        //优惠
        $discount_money   = $params['supplierOrder']['discount_money'];
        $type             = $orderStatus != 2 ? 1 : 3;
        $goodsMoneyOrigin = 0;
        foreach ($goodsList as $v) {
            $goodsMoneyOrigin = bcadd($goodsMoneyOrigin, bcmul($v['price'], $v['quantity'], 2), 2);
        }
        $hasDiscountMoney = $order_pay_amount = $channel_discount_amount = $goodsTotalMoney = 0;
        $goodsNum         = count($goodsList);
        foreach ($goodsList as $key => $item) {
            $price            = (string)$item['price'];
            $spec_num         = (string)$item['spec_num'];
            $quantity         = (string)$item['quantity'];
            $supplier_skucode = $item['supplier_skucode'];
            $goods_type       = $item['goods_type'];
            $buyNum           = $spec_num * $quantity;

            //分摊优惠金额到单个商品
            if ($key == $goodsNum - 1) {
                $skuDiscountAmountTotal = bcsub((string)$discount_money, (string)$hasDiscountMoney, 2);
            } else {
                $singleGoodsTotalPrice  = bcmul((string)$price, (string)$quantity, 2);
                $scale                  = bcdiv((string)$singleGoodsTotalPrice, (string)$goodsMoneyOrigin, 4);
                $skuDiscountAmountTotal = bcmul((string)$scale, (string)$discount_money, 2);
                $hasDiscountMoney       = bcadd((string)$hasDiscountMoney, (string)$skuDiscountAmountTotal, 2);
            }
            //原始销售单价
            $singlePrice = bcdiv((string)$price, (string)$spec_num, 3);
            $singlePrice = ceil(bcmul((string)$singlePrice, '100', 1));
            $singlePrice = bcdiv((string)$singlePrice, '100', 2);
            //向上取整3位收上去的
            $singleNewTotalPrice = bcmul((string)$singlePrice, (string)$buyNum, 2);
            $signleOldTotalPrice = bcmul((string)$price, (string)$quantity, 2);
            $diff_price          = bcsub((string)$singleNewTotalPrice, (string)$signleOldTotalPrice, 2);
            //优惠金额还需要加上补差金额否则无法过去
            $skuDiscountAmountTotal = bcadd((string)$diff_price, (string)$skuDiscountAmountTotal, 2);
            //单个商品实际支付总金额
            $sku_pay_amount = bcsub((string)$singleNewTotalPrice, (string)$skuDiscountAmountTotal, 2);
            $refundGoods[]  = [
                'oid' => $item['id'],//平台订单子订单编号（与推送trade_push的oid保持一致，对应旺店通ERP内原始子订单编号）可推送部分子订单
                'num' => $type == 1 ? $sku_pay_amount : $buyNum,//退货货品数量（如果是type=1 退款   2 退款不退货类型，此处代表退款数量）
            ];
        }
        $refund_list[] = [
            'platform_id' => $this->config['platformId'],//接口请求时的业务参数platform_id 固定值127
            'shop_no'     => $this->config['shopNo'],
            'tid'         => $params['supplierOrder']['supplier_dsn'],
            'refund_no'   => 'TK' . $params['supplierOrder']['supplier_dsn'],//平台退款/退货单单号，保证唯一
            'type'        => $type,//type=1：退款(未发货退款)；type=2：退款不退货；type=3：退货
            'status'      => 'wait_seller_agree',//可选值：wait_seller_agree（申请退款），  seller_refuse（拒绝退款） ， closed（退款关闭） ，goods_returning（待退货） goods_receiving（待收货），success（退款成功）
            'refund_fee'  => $params['supplierOrder']['real_total_money'],
            'buyer_nick'  => $params['supplierOrder']['buyer_realname'],
            'refund_time' => date('Y-m-d H:i:s'),//单据创建时间,时间格式：yyyy-MM-dd HH:mm:ss
            'reason'      => $params['order']['exceptReason'] ?? '人工运营处理售后退款',//原因
            'desc'        => $params['order']['exceptReason'] ?? '人工运营处理售后退款',//描述
            'order_list'  => $refundGoods
        ];
        $data          = [
            'method' => 'orderAfterSale',
            'data'   => [
                'api_refund_list' => json_encode($refund_list, JSON_UNESCAPED_UNICODE)
            ],
        ];
        return $this->execute($data);
    }

    /**
     * 签名
     * @param $data
     * @return string
     */
    public function sign($data) {
        ksort($data);
        $arr = [];
        foreach ($data as $key => $val) {
            if ($key == 'sign') continue;
            if (count($arr))
                $arr[] = ';';
            $arr[] = sprintf("%02d", iconv_strlen($key, 'UTF-8'));
            $arr[] = '-';
            $arr[] = $key;
            $arr[] = ':';
            $arr[] = sprintf("%04d", iconv_strlen($val, 'UTF-8'));
            $arr[] = '-';
            $arr[] = $val;
        }
        // return implode('', $arr);
        return md5(implode('', $arr) . $this->config['appSecret']);
    }

    /**
     * CURL请求
     * @param $params
     * @return array
     */
    public function execute($param = []) {
        $method  = $param['method'];
        $reqData = $param['data'];
        if (!isset($this->config[$method])) {
            return Normal::returnCode(Normal::FAIL, "请求路由未设置");
        }
        if (empty($param['data'])) {
            return Normal::returnCode(Normal::FAIL, "数据不能为空");
        }
        $baseUrl = $this->config['baseUrl'] ?? '';
        if (empty($baseUrl)) {
            return Normal::returnCode(Normal::FAIL, "接口请求地址不能为空");
        }
        $url                  = $this->config['baseUrl'] . $this->config[$method];
        $reqData['sid']       = $this->config['sid'];
        $reqData['appkey']    = $this->config['appkey'];
        $reqData['timestamp'] = time();
        $reqData['sign']      = $this->sign($reqData);
        $parameter            = [
            [
                'tag'    => self::SUPPLIER_CODE . '_' . $param['method'],
                'uid'    => IdService::instance()->getOtherID(),
                'url'    => $url,
                'data'   => $reqData,
                'method' => 'post',
                'format' => 'x-www-form-urlencoded',
                'header' => [],
            ],
        ];
        // $resTask          = TaskWorker::instance()->addTask($parameter);
        if (in_array($param['method'], ['queryGoods', 'queryStock', 'orderExpress', 'orderExpressAck'])) {
            $resTask = TaskWorker::instance()->addTask($parameter, 'handleDiableTask');
        } else {
            $resTask = TaskWorker::instance()->addTask($parameter);
        }
        $resTask = current($resTask);
        $result  = @json_decode($resTask['response']['result'], true);
        if (isset($result['code']) && $result['code'] == 0) {
            return Normal::returnCode(Normal::SUC, "操作成功", $result);
        }
        $msg = $result['message'] ?? '请求失败';
        return Normal::returnCode(Normal::FAIL, $msg, $result);
    }

}